package com.wimcorp.magic.stack;

import java.rmi.RemoteException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;

import com.wimcorp.magic.abilities.Ability;
import com.wimcorp.magic.abilities.UndoAbility;
import com.wimcorp.magic.board.CardAccessor;
import com.wimcorp.magic.cards.PlayingCard;
import com.wimcorp.magic.players.Player;
import com.wimcorp.magic.spell.Castable;

public class Stack extends CardAccessor implements Runnable {

	private final BlockingQueue<Castable> queue;
	private boolean resolve = false;
	private CountDownLatch latch;

	public Stack() {
		queue = new BlockingLifoQueue<Castable>();
	}

	public void run() {
		try {
			while (queue.size() != 0) {
				consume(queue.take());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		latch.countDown();

	}

	private void consume(Castable castable) {
		Player player = castable.getPlayer();
		Player opponent = player.getOpponent();
		try {
			castable.cast();
			if (!(castable instanceof UndoAbility)) {
				messagePlayer(player, String.format("You played : %s", castable.getCastDescription()));
				messagePlayer(opponent,
						String.format("Your opponent %s played : %s", player.getName(), castable.getCastDescription()));
			}
		} catch (Exception e) {
			messagePlayer(player, e.getMessage());
			e.printStackTrace();
		}
	}

	private void messagePlayer(Player p, String message) {
		try {
			p.getUserInterface().message(message);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public int size() {
		return queue.size();
	}

	public void addOnStack(Castable castable) {
		queue.add(castable);
	}

	public Set<PlayingCard> getCards() {
		Set<PlayingCard> cards = new HashSet<>();
		for (Castable c : queue) {
			if (c instanceof PlayingCard) {
				cards.add((PlayingCard) c);
			} else if (c instanceof Ability) {
				cards.add(((Ability) c).getCard());
			}
		}
		return cards;
	}

	public PlayingCard getCard(long id) {
		PlayingCard card = null;
		Set<PlayingCard> cards = getCards();
		for (PlayingCard c : cards) {
			if (c.getIdentifier() == id) {
				card = c;
				break;
			}
		}
		return card;
	}

	public void remove(PlayingCard card) {
		queue.remove(card);
	}

	public void resolve() {
		Thread t = new Thread(this);
		latch = new CountDownLatch(1);
		t.start();
		try {
			latch.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

	}

	public boolean isResolved() {
		return !resolve;
	}

}